package com.example.chat.ws;

import java.io.IOException;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.http.HttpSession;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import com.example.chat.config.GetHttpSessionConfig;
import org.springframework.stereotype.Component;

import com.alibaba.fastjson2.JSON;
import com.example.chat.utils.MessageUtils;
import com.example.chat.wsResultMessage.entity.Message;

import lombok.extern.slf4j.Slf4j;

/**
 * 处理具体websocket消息的接口
 */
@Slf4j
@ServerEndpoint(value = "/chat",configurator = GetHttpSessionConfig.class)
@Component
public class ChatEndpoint {

    /**
     * 线程安全，
     * String：存储某个用户的标识比如HttpSession session :String username = session.getAttribute("user")
     * 这里的username就可以作为标识
     * session ：WebSocket 中的 session 是一个通道对象，你要通过它来发消息给客户端，否则服务器不知道发给谁。
     */
    private static final Map<String,Session> onlineUsers = new ConcurrentHashMap<>();

    private HttpSession httpSession;
    /**
     * 连接建立后被调用
     * @param session Session
     * @param config EndpointConfig
     */
    @OnOpen
    public void onOpen(Session session, EndpointConfig config){
        this.httpSession = (HttpSession) config.getUserProperties()
                .get(HttpSession.class.getName());
        String user = (String) httpSession.getAttribute("user");

        // 先判断这个用户是否已经登录了，如果登录了，就踢掉之前的连接
        if (onlineUsers.containsKey(user)) {
            Session oldSession = onlineUsers.get(user);
            try {
                oldSession.getBasicRemote().sendText("你已在其他设备登录，本设备将被下线");
                oldSession.close(); // 关闭旧连接
                log.info("用户【{}】重复登录，已踢掉旧连接", user);
            } catch (IOException e) {
                log.warn("踢掉旧连接失败：{}", e.getMessage());
            }
        }

        // 保存新的连接
        onlineUsers.put(user, session);
        log.info("用户【{}】已上线", user);

        // 广播所有用户列表
        String message = MessageUtils.getMessage(true, null, getFriends());
        broadcastAllUsers(message);
    }

    private Set<String> getFriends(){
        return  onlineUsers.keySet();
    }

    /**
     * 广播消息，推送给其他所有用户
     * @param message 消息
     */
    private void broadcastAllUsers(String message){
        String user = (String)httpSession.getAttribute("user");
        log.info("当前用户【{}】，广播消息给其他所有用户消息：【{}】",user,message);

        Set<Map.Entry<String, Session>> entries = onlineUsers.entrySet();
        for (Map.Entry<String, Session> entry : entries) {
            //其实是循环获取到所有用户的session"通道"
            Session session = entry.getValue();
            //发送消息
            try {
                session.getBasicRemote().sendText(message);
            } catch (IOException e) {
                // e.printStackTrace();
                //记录日志
                log.info("当前用户【{}】，广播消息失败，失败原因为：【{}】",user,e.getMessage());
            }
        }
    }

    /**
     * 浏览器发送消息到服务端，接收到客户端发送的数据时该方法被调用
     * @param message 消息 数据格式: {"toName":"张三","message":"你好..."}
     */
    @OnMessage
    public void onMessage(String message){
        //将消息推送给指定的用户
        Message msg = JSON.parseObject(message, Message.class);
        //获取指定用户的用户名
        String toName = msg.getToName();
        //获取发送的消息
        String sendMsg = msg.getMessage();
        //获取当前用户的用户名
        String currentUser = (String)httpSession.getAttribute("user");
        String msgStr = MessageUtils.getMessage(false, currentUser, sendMsg);

        Session session = onlineUsers.get(toName);
        try {
            session.getBasicRemote().sendText(msgStr);
            log.info("当前用户【{}】,给指定用户【{}】,发送了消息:【{}】",currentUser,toName,sendMsg);
        } catch (IOException e) {
            // e.printStackTrace();
            //记录日志
            log.info("当前用户【{}】,给指定用户【{}】,发送了消息失败，失败原因:【{}】",currentUser,toName,e.getMessage());
        }
    }

    /**
     * 连接关闭时被调用
     * @param session Session
     */
    @OnClose
    public void onClose(Session session){
        //1、 从 onlineUsers 中删除当前用户的session对象
        String user = (String)httpSession.getAttribute("user");
        onlineUsers.remove(user);
        log.info("用户【{}】已下线",user);

        //2、通知其他所有的用户，当前用户下线了。
        String message = MessageUtils.getMessage(true, null, getFriends());
        broadcastAllUsers(message);
    }
}
